package com.ayf.payment.game.api.task.autoReissue;

import com.ayf.payment.game.api.dto.PartitionDTO;
import com.ayf.payment.game.api.dto.criteria.ReissueRecordCriteria;
import com.ayf.payment.game.api.service.PartitionService;
import com.ayf.payment.game.api.service.ReissueRecordService;
import com.ayf.payment.game.api.utils.CommonResult;
import com.ayf.payment.game.api.utils.DateUtils;
import com.ayf.payment.game.api.utils.SnowFlakeUtils;
import com.ayf.payment.integral.dto.IntegralExchangeOrderDTO;
import com.ayf.payment.integral.dto.MyPageDTO;
import com.ayf.payment.integral.service.remote.IntegralRemoteService;
import com.ayf.payment.integral.vo.IntegralVO;
import com.swwx.charm.commons.lang.utils.LogPortal;
import com.swwx.charm.zookeeper.exception.GetLockFailedException;
import com.swwx.charm.zookeeper.exception.ReleaseLockFailedException;
import com.swwx.charm.zookeeper.lock.DistributedLock;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @Author: zh-liang
 * @Date : 2020-09-16
 */
@Component
public abstract class GoodAutoReissueTaskImpl {

    private static final String PLATFORM_CODE = System.getenv("GAME_AYFPAY_PLATFORM_CODE");

    @Reference(version = "1.0.0",check = false)
    private IntegralRemoteService integralRemoteService;
    @Resource
    private ReissueRecordService reissueRecordService;
    @Resource
    private PartitionService partitionService;

    protected void run(DistributedLock distributedLock) {
        boolean lock = false;
        String traceId = SnowFlakeUtils.getTraceId();
        String lockName = this.getClass().getSimpleName().concat(System.getenv("XY_GAME_PAY_LOCK_FLAG"));
        try {
            if (lock = distributedLock.getLock(lockName)) {
                LogPortal.info("{} >>> get lock and run {} ", traceId, lockName);
                IntegralVO integralVO = new IntegralVO();
                integralVO.setPageNum(1);
                integralVO.setPageSize(1);
                integralVO.setGoodType(2);
                integralVO.setStatus(0);
                integralVO.setPlatformCode(PLATFORM_CODE);
                integralVO.setStartTime(DateUtils.getDayBeforeDay(7));
                integralVO.setEndTime(new Date());
                MyPageDTO<IntegralExchangeOrderDTO> pageDTO = integralRemoteService.integralOrderList(integralVO);
                long count = pageDTO.getTotal();
                LogPortal.info("{} >>> [{}]处理总条数[{}]", traceId, lockName, count);
                if (count <= 0) {
                    return;
                }
                final int pageSize = 100;
                long totalPage = count % pageSize == 0 ? (count / pageSize) : (count / pageSize) + 1;
                for (int i = 0; i < totalPage; i++) {
                    IntegralVO vo = new IntegralVO();
                    vo.setPageNum(i + 1);
                    vo.setPageSize(pageSize);
                    vo.setGoodType(2);
                    vo.setStatus(0);
                    vo.setPlatformCode(PLATFORM_CODE);
                    vo.setStartTime(DateUtils.getDayBeforeDay(7));
                    vo.setEndTime(new Date());
                    MyPageDTO<IntegralExchangeOrderDTO> myPageDTO = integralRemoteService.integralOrderList(vo);
                    final List<IntegralExchangeOrderDTO> list = myPageDTO.getRecords();
                    LogPortal.info("{} >>> {} 开始处理第[{}/{}]页自动补发订单，共[{}]条", traceId, lockName, (i + 1), totalPage, list.size());
                    //初始化信号
                    final CountDownLatch countDownLatch = new CountDownLatch(list.size());
                    for (int n = 0; n < list.size(); n++) {
                        IntegralExchangeOrderDTO dto = list.get(n);
                        int a = i + 1;
                        int m = n + 1;
                        submit(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    LogPortal.info("{} >>> {} 开始处理第[{}]页,第[{}/{}]条自动补发订单", traceId, lockName, a, m, list.size());
                                    CommonResult<T> result = manualTopUp(dto);
                                    if (result.getCode() == 200 || result.getCode() == 801) {
                                        IntegralVO vo1 = new IntegralVO();
                                        vo1.setExchangeOrderId(dto.getId());
                                        vo1.setStatus(1);
                                        integralRemoteService.updateOrderStatus(vo1);
                                        LogPortal.info("{} >>> {} 第[{}]页,第[{}/{}]条自动补发订单处理完成,code返回:{},玩家id:{},订单号:{}",
                                                traceId, lockName, a, m, list.size(), result.getCode(), dto.getPlayerId(), dto.getSerialNo());
                                    } else {
                                        IntegralVO vo1 = new IntegralVO();
                                        vo1.setExchangeOrderId(dto.getId());
                                        vo1.setStatus(3);
                                        vo1.setFailDes(result.getMsg() + "，错误码：" + traceId);
                                        integralRemoteService.updateOrderStatus(vo1);
                                        LogPortal.error("{} >>> {} 处理第[{}]页,第[{}/{}]条自动补发处理失败,code返回:{}，错误描述:{},玩家id:{},订单号:{}",
                                                traceId, lockName, a, m, list.size(), result.getCode(), result.getMsg(), dto.getPlayerId(), dto.getSerialNo());
                                    }
                                } catch (Exception e) {
                                    LogPortal.error("{} >>> {} 处理第[{}]页,第[{}/{}]条自动补发订单时发生了{}异常，错误描述:{},玩家id:{},订单号:{}",
                                            traceId, lockName, a, m, list.size(), e.getClass().getName(), e.getMessage(), dto.getPlayerId(), dto.getSerialNo());
                                } finally {
                                    //线程执行完成之后标记信号表示完成
                                    countDownLatch.countDown();
                                }
                            }
                        });
                    }
                    //等待线程池任务执行完毕
                    countDownLatch.await();
                }
            } else {
                LogPortal.info("{} >>> {} do not get lock ", traceId, lockName);
            }
        } catch (GetLockFailedException e) {
            LogPortal.error("{} >>> [DISTRIBUTED_LOCK_ACQUIRE_FAILED_ZK] 分布式锁获取失败,锁名称为:{}", traceId, lockName);
            LogPortal.error("{} >>> {} 获取lock失败", e, traceId, lockName);
        } catch (Exception e) {
            LogPortal.error("{} >>> {} 任务执行出错 ", e, traceId, lockName);
        } finally {
            if (lock) {
                LogPortal.info(traceId + " >>> " + lockName + " finished.");
                try {
                    distributedLock.releaseLock(lockName);
                } catch (ReleaseLockFailedException e) {
                    LogPortal.error("{} >>> [DISTRIBUTED_LOCK_RELEASE_FAILED_ZK] 分布式锁释放失败,锁名称为:{}", traceId, lockName);
                    LogPortal.error("{} >>> {} 释放失败", e, traceId, lockName);
                }
            }
        }
    }

    public abstract void submit(Runnable task);

    /**
     * 手动充值
     */
    private CommonResult<T> manualTopUp(IntegralExchangeOrderDTO dto) {
        ReissueRecordCriteria manualCriteria = new ReissueRecordCriteria();
        manualCriteria.setOperateIp("-");
        manualCriteria.setOperatorId(0);
        manualCriteria.setChannelName("商品自动补发");
        manualCriteria.setCode("AUTO_REISSUE_GOOD");

        manualCriteria.setManualType("1_2_3_4");
        manualCriteria.setRechargeNumber(dto.getShippingPeople());
        manualCriteria.setRechargeAmount(dto.getReissueAmount());
        manualCriteria.setPartitionId(dto.getPartitionId());
        manualCriteria.setRatId(dto.getSerialNo());
        PartitionDTO partitionInfoById = partitionService.getPartitionInfoById(dto.getPartitionId());
        manualCriteria.setMerchantId(partitionInfoById.getMerchantId());

        manualCriteria.setSerialNumber("B" + SnowFlakeUtils.getUniqueId());
        String reissueRecordId = reissueRecordService.manualTopUp(manualCriteria);
        return reissueRecordService.rechargeGetWay(partitionInfoById.getMerchantId(), reissueRecordId);
    }
}
